package vooga.core;

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.geom.Point2D;
import java.util.HashMap;
import java.util.Iterator;
import java.util.TreeMap;

import javax.swing.JPanel;

/**
 * LayerSet stores a set of Layers.  Because it also extends JPanel, it can be added to a
 * container as a component to be displayed
 * @author Congyi Wu
 *
 */
public class LayerSet extends JPanel implements Iterable<Layer> {
	/*
	 * We need both a SortedMap and a HashMap because we would like O(1) in
	 * order iteration through the Layers and O(1) lookup of layers by z-order
	 * as well. It isn't much less efficient to keep two maps, assuming that
	 * we won;t be adding and removing Layers all the time
	 */
	protected java.util.SortedMap<Integer, Layer> mySortedLayers; // layers sorted by z-order
	protected java.util.HashMap<Integer, Layer> myUnsortedLayers; // helps get layers by zOrder in O(1) time
	
	public LayerSet()
	{
		super(true);
		
		//this is necessary bc images must have dimensions > 1 x 1
		setSize(1,1);
		setMinimumSize(new java.awt.Dimension(1,1));
		
		mySortedLayers = new TreeMap<Integer, Layer>();
		myUnsortedLayers = new HashMap<Integer, Layer>();
		
		//make sure the layers are resized when the pane is
		addComponentListener(new ComponentAdapter()
		{
			public void componentResized(ComponentEvent e)
			{
				for(Layer l : mySortedLayers.values())
					l.notifyContainerSizeChanged(getSize());
			}
		});
	}
	
	public void add(Layer l)
	{
		l.notifyContainerSizeChanged(getSize());
		mySortedLayers.put(l.getZOrder(), l);
		myUnsortedLayers.put(l.getZOrder(), l);
	}
	
	/**
	 * 
	 * @param zOrder
	 * @return the layer with the z-order zOrder
	 */
	public Layer get(int zOrder)
	{
		return myUnsortedLayers.get(zOrder);
	}
	
	/**
	 * remove a layer
	 * @param l the Layer to remove
	 */
	public void remove(Layer l)
	{
		mySortedLayers.remove(l.getZOrder());
		myUnsortedLayers.remove(l.getZOrder());
	}
	
	public void remove(int zOrder)
	{
		mySortedLayers.remove(zOrder);
		myUnsortedLayers.remove(zOrder);
	}
	
	public void clear()
	{
		mySortedLayers.clear();
		myUnsortedLayers.clear();
	}
	
	public void paint(Graphics g)
	{
		g.clearRect(0, 0, getWidth(), getHeight());
		for(Layer l : mySortedLayers.values())
			g.drawImage(l.getViewableImage(), (int)getPhysicalLeft(l), (int)getPhysicalTop(l), (int)getPhysicalWidth(l), (int)getPhysicalHeight(l), this);
	}

	/**
	 * Convert a physical coordinate in this LayerSet pane to a coordinate in this Layer's abstract
	 * coordinate system
	 * @param physicalPoint the physical coordinate to convert to an abstract coordinate
	 */
	public Point2D.Double getAbstractPoint(Layer layer, Point2D.Double physicalPoint)
	{
		return new Point2D.Double(getAbstractX(layer, physicalPoint.x), getAbstractY(layer, physicalPoint.y));
	}

	public double getAbstractX(Layer layer, double physicalX)
	{
		return layer.getAbstractCropBounds().getX() + physicalX * layer.getAbstractCropBounds().getWidth() / getWidth();
	}

	public double getAbstractY(Layer layer, double physicalY)
	{
		return layer.getAbstractCropBounds().getX() + physicalY * layer.getAbstractCropBounds().getHeight() / getHeight();
	}

	/**
	 * Convert a coordinate in this Layer's abstract coordinate system to a
	 * physical coordinate
	 * @param abstractPoint the abstract point to convert to a physical point
	 */
	public Point2D.Double getPhysicalPoint(Layer layer, Point2D.Double abstractPoint)
	{
		return new Point2D.Double(getPhysicalX(layer, abstractPoint.getX()), getPhysicalY(layer, abstractPoint.getY()));
	}

	public double getPhysicalX(Layer layer, double abstractX)
	{
		return (abstractX - layer.getAbstractCropBounds().getX()) * getWidth() / layer.getAbstractCropBounds().getWidth();
	}

	public double getPhysicalY(Layer layer, double abstractY)
	{
		return (abstractY - layer.getAbstractCropBounds().getY()) * getHeight() / layer.getAbstractCropBounds().getHeight();
	}
	
	/**
	 * 
	 * @param layer
	 * @return the physical top y position of layer on this LayerSet
	 */
	public double getPhysicalTop(Layer layer)
	{
		return layer.getPhysicalPercentFromTop() * getHeight();
	}

	/**
	 * 
	 * @param layer
	 * @return the physical left x position of layer on this LayerSet
	 */
	public double getPhysicalLeft(Layer layer)
	{
		return layer.getPhysicalPercentFromLeft() * getWidth();
	}
	
	/**
	 * 
	 * @param layer
	 * @return the physical height of layer on this LayerSet
	 */
	public double getPhysicalHeight(Layer layer)
	{
		return layer.getPhysicalPercentHeight() * getHeight();
	}

	/**
	 * 
	 * @param layer
	 * @return the physical width of layer on this LayerSet
	 */
	public double getPhysicalWidth(Layer layer)
	{
		return layer.getPhysicalPercentWidth() * getWidth();
	}

	public Iterator<Layer> iterator() {
		// TODO Auto-generated method stub
		return mySortedLayers.values().iterator();
	}
}
